package com.canvasoft.servlets;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
import org.apache.catalina.websocket.WsOutbound;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.canvasoft.servlets.messages.ConnectionInfoMessage;
import com.canvasoft.servlets.messages.MessageInfoMessage;
import com.canvasoft.servlets.messages.StatusInfoMessage;
import com.google.gson.Gson;

@WebServlet(urlPatterns = "/hotelws")
public class WebSocketHotelServlet extends WebSocketServlet {	
	/**
	 * SERIAL ID
	 */
	private static final long serialVersionUID = 6797028221841562147L;
	private static final Logger log = LoggerFactory.getLogger(WebSocketHotelServlet.class);
        private static final Map<String, BattleConnection> connections = new HashMap<String, BattleConnection>();

	@Override
	protected StreamInbound createWebSocketInbound(String subProtocol, HttpServletRequest request) {
			log.debug("Ejecutando: createWebSocketInbound");
            final String connectionId = request.getSession().getId();
            final String email = request.getParameter("email");
            final String password = request.getParameter("password");
            return new BattleConnection(connectionId, email, password);
	}
	
	private static class BattleConnection extends MessageInbound {
            private final String connectionId;
            private final String email;
            private final String password;
            private final Gson jsonProcessor;
        
            private BattleConnection(String connectionId, String email, String password) {
                this.connectionId = connectionId;
                this.email = email;
                this.password = password;
                this.jsonProcessor = new Gson();
                /*
                 * implementar el login con el servicio de .net 
                 */
            }

            @Override
            protected void onOpen(WsOutbound outbound) {
                sendConnectionInfo(outbound);
                sendStatusInfoToOtherUsers(new StatusInfoMessage(email, StatusInfoMessage.STATUS.CONNECTED));
                connections.put(connectionId, this);
            }

            @Override
            protected void onClose(int status) {
                sendStatusInfoToOtherUsers(new StatusInfoMessage(email, StatusInfoMessage.STATUS.DISCONNECTED));
                connections.remove(connectionId);
            }

            @Override
            protected void onBinaryMessage(ByteBuffer byteBuffer) throws IOException {
                throw new UnsupportedOperationException("Binary messages not supported");
            }

            @Override
            protected void onTextMessage(CharBuffer charBuffer) throws IOException {
                /*
                 * Implementar la conexion con el webservice de .net
                 */
                final MessageInfoMessage message = jsonProcessor.fromJson(charBuffer.toString(), MessageInfoMessage.class);
                final BattleConnection destinationConnection = getDestinationUserConnection(message.getMessageInfo().getTo());
                if (destinationConnection != null) {
                    final CharBuffer jsonMessage = CharBuffer.wrap(jsonProcessor.toJson(message));
                    destinationConnection.getWsOutbound().writeTextMessage(jsonMessage);
                } else {
                    log.warn("Se está intentando enviar un mensaje a un usuario no conectado");
                }
            }

            public String getEmail() {
                return email;
            }

            private void sendConnectionInfo(WsOutbound outbound) {
                final List<String> activeUsers = getActiveUsers();
                final ConnectionInfoMessage connectionInfoMessage = new ConnectionInfoMessage(email, activeUsers);
                try {
                    outbound.writeTextMessage(CharBuffer.wrap(jsonProcessor.toJson(connectionInfoMessage)));
                } catch (IOException e) {
                    log.error("No se pudo enviar el mensaje", e);
                }
            }

            private List<String> getActiveUsers() {
                final List<String> activeUsers = new ArrayList<String>();
                for (BattleConnection connection : connections.values()) {
                    activeUsers.add(connection.getEmail());
                }
                return activeUsers;
            }

            private void sendStatusInfoToOtherUsers(StatusInfoMessage message) {
                final Collection<BattleConnection> otherUsersConnections = getAllChatConnectionsExceptThis();
                for (BattleConnection connection : otherUsersConnections) {
                    try {
                        connection.getWsOutbound().writeTextMessage(CharBuffer.wrap(jsonProcessor.toJson(message)));
                    } catch (IOException e) {
                        log.error("No se pudo enviar el mensaje", e);
                    }
                }
            }

            private Collection<BattleConnection> getAllChatConnectionsExceptThis() {
                final Collection<BattleConnection> allConnections = connections.values();
                allConnections.remove(this);
                return allConnections;
            }

            private BattleConnection getDestinationUserConnection(String destinationUser) {
                for (BattleConnection connection : connections.values()) {
                    if (destinationUser.equals(connection.getEmail())) {
                        return connection;
                    }
                }
                return null;
            }
		
	}
}
